/*
 * Copyright (c) 2008, Xiong Yingfei, the University of Tokyo All rights
 * reserved.
 */
package ac.jp.u_tokyo.SyncLib.interactive;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.antlr.runtime.RecognitionException;

import ac.jp.u_tokyo.SyncLib.Mod;
import ac.jp.u_tokyo.SyncLib.NullMod;
import ac.jp.u_tokyo.SyncLib.Sync;
import ac.jp.u_tokyo.SyncLib.SyncFactory;
import ac.jp.u_tokyo.SyncLib.SynchronizationFailedException;
import ac.jp.u_tokyo.SyncLib.dictionaries.DynamicGet;
import ac.jp.u_tokyo.SyncLib.modParser.ModParser;
import ac.jp.u_tokyo.SyncLib.util.Helper;
import ac.jp.u_tokyo.SyncLib.valueParser.ValueParser;

public class Console
{
	public static Sync findSyncByName(Class syncFactoryClass, String name)
	{
		Method m;
		try
		{
			m = syncFactoryClass.getMethod("get" + name + "Factory");
			SyncFactory factory = (SyncFactory) m.invoke(null);
			return factory.create();
		}
		catch (Exception e)
		{
			return null;
		}
	}

	private static final String usage = "command: \n" + "name\n" + "val\n"
			+ "switch synchronizerName\n" + "sync modifcations\n"
			+ "resync values; modifications\n" + "trace [off|on]\n" + "exit";

	public static void interactiveExec(Class syncFactoryClass)
			throws IOException
	{
		Map<String, Object[]> vals = new HashMap<String, Object[]>();
		String curSync = null;
		Map<String, Sync> syncs = new HashMap<String, Sync>();
		Map<String, Boolean> isInitialized = new HashMap<String, Boolean>();

		{
			String newSyncName = "Main";
			curSync = switchTo(syncFactoryClass, curSync, newSyncName, vals,
					syncs, isInitialized);
		}

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		while (true)
		{
			System.out.print(">>");
			String input = br.readLine();
			String command = input.split("[ \t]")[0];
			if (curSync == null
					&& (command.compareToIgnoreCase("sync") == 0
							|| command.compareToIgnoreCase("resync") == 0
							|| command.compareToIgnoreCase("name") == 0 || command
							.compareToIgnoreCase("val") == 0))
			{
				System.out.println("no synchronizer loaded.");
				continue;
			}
			if (!isInitialized.get(curSync)
					&& (command.compareToIgnoreCase("sync") == 0 || command
							.compareToIgnoreCase("val") == 0))
			{
				System.out.println("execute resync first.");
				continue;
			}

			if (command.compareToIgnoreCase("val") == 0)
			{
				System.out.println(Helper.toString(vals.get(curSync)));
			}
			else if (command.compareToIgnoreCase("name") == 0) {
				System.out.println(curSync);
			}
			else if (command.compareToIgnoreCase("switch") == 0)
			{
				String newSyncName;
				try
				{
					newSyncName = input.split("[ \t]")[1].trim();
				}
				catch (ArrayIndexOutOfBoundsException e)
				{
					System.out.println(usage);
					continue;
				}

				if (syncs.containsKey(newSyncName))
				{
					curSync = newSyncName;
				}
				else
				{
					curSync = switchTo(syncFactoryClass, curSync, newSyncName,
							vals, syncs, isInitialized);
				}
			}
			else if (command.compareToIgnoreCase("sync") == 0)
			{
				Mod[] mods;
				try
				{
					mods = ModParser.parse(input.substring(command.length()));
					Mod[] result = syncs.get(curSync).synchronize(mods);
					Helper.applyMods(vals.get(curSync), result);
					System.out.println(Helper.toString(result));
				}
				catch (RecognitionException e)
				{
					System.out.println("cannot read input modification!");
					if (TraceAspect.showTrace()) e.printStackTrace(System.out);
				}
				catch (SynchronizationFailedException e)
				{
					System.out.println("Synchronization failed!");
					if (TraceAspect.showTrace()) e.printStackTrace(System.out);
				}
			}
			else if (command.compareToIgnoreCase("resync") == 0)
			{
				Mod[] mods;
				String modString;
				String valueString;
				try
				{
					modString = input.substring(command.length()).split(";")[1];
					valueString = input.substring(command.length()).split(";")[0];
				}
				catch (ArrayIndexOutOfBoundsException e)
				{
					System.out.println(usage);
					continue;
				}

				try
				{
					mods = ModParser.parse(modString);
					Object[] values = ValueParser.parse(valueString);
					Mod[] result = syncs.get(curSync).resynchronize(values, mods);
					vals.put(curSync, values);
					Helper.applyMods(vals.get(curSync), result);
					System.out.println(Helper.toString(result));
					isInitialized.put(curSync, true);
				}
				catch (RecognitionException e)
				{
					System.out.println("cannot read input modification!");
					if (TraceAspect.showTrace()) e.printStackTrace(System.out);
				}
				catch (SynchronizationFailedException e)
				{
					System.out.println("Synchronization failed!");
					if (TraceAspect.showTrace()) e.printStackTrace(System.out);
				}
			}
			else if (command.compareToIgnoreCase("trace") == 0)
			{
				if (input.contains("on"))
					TraceAspect._showTrace = true;
				else
					TraceAspect._showTrace = false;
			}
			else if (command.compareToIgnoreCase("exit") == 0)
			{
				return;
			}
			else
			{
				System.out.println(usage);
			}
		}
	}

	private static String switchTo(Class syncFactoryClass, String curSync,
			String newSyncName, Map<String, Object[]> vals,
			Map<String, Sync> syncs, Map<String, Boolean> isInitialized)
	{
		Sync s = findSyncByName(syncFactoryClass, newSyncName);
		if (s == null)
		{
			System.out.println("Cannot create synchronizer " + newSyncName);
		}
		else
		{
			curSync = newSyncName;
			syncs.put(curSync, s);
			vals.put(curSync, new Object[s.getParaCount()]);
			isInitialized.put(curSync, false);
		}
		return curSync;
	}

//	private static Mod[] createNullMods(int length)
//	{
//		Mod[] mods = new Mod[length];
//		for (int i = 0; i < length; i++)
//		{
//			mods[i] = NullMod.INSTANCE;
//		}
//		return mods;
//	}
	
	public static SyncFactory getMainFactory() {
		return new SyncFactory() {

		public Sync create()
		{
			return new DynamicGet(true, false);
		}};
	}

	public static void main(String[] args) throws IOException
	{
		Console.interactiveExec(Console.class);
	}
}
